package com.wind;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.EntityNotFoundException;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.PreparedQuery;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.Query.FilterOperator;
import com.google.appengine.api.datastore.Text;

public class WindDatastoreServiceAppEngine extends WindDatastoreService {

	private static WindDatastoreServiceAppEngine instance = new WindDatastoreServiceAppEngine();
	
	public static WindDatastoreServiceAppEngine getInstance() {
		return instance;
	}
	
	private static DatastoreService service = null;
	
	private static DatastoreService service() {
		if(service==null) {
			service = DatastoreServiceFactory.getDatastoreService();
		}
		return service;
	}
	
	public String getCode() {
		return "appEngine";
	}
	
	public void executeDelete(JSON jsonRequest, JSON jsonResponse, JSON jsonErrors) {
		String keys = jsonRequest.getString("keys");
		if(keys !=null ) {
			String[] arrayKeys = keys.split(",");
			for(int i=0; i<arrayKeys.length; i++) {
				String strKey = arrayKeys[i];
				Key key = KeyFactory.stringToKey(strKey);
				service().delete(key);
			}
		}
	}

	public void executeInsert(JSON jsonRequest, JSON jsonResponse, JSON jsonErrors, WindEntity windEntity) {
		Entity appEngineEntity = windEntityToAppEngineEntity(windEntity);
		service().put(appEngineEntity);
	}

	public void executeUpdate(JSON jsonRequest, JSON jsonResponse, JSON jsonErrors, WindEntity windEntity) {
		Entity appEngineEntity = windEntityToAppEngineEntity(windEntity);
		service().put(appEngineEntity);
	}

	public void executeList(JSON jsonRequest, JSON jsonResponse, JSON jsonErrors) {
		Entity entity = getByKey(jsonRequest.getString("key"));
		if(entity!=null) {
			JSON jsonEntity = jsonResponse.addObject("entity");
			entityToJSON(entity, jsonEntity);
		} else {
			JSON rows = jsonResponse.addArray("rows");
			
			
			
			String type = jsonRequest.getString("type");
			Query query = new Query(type);
			addRequestSortsToQuery(jsonRequest, query);
			addRequestFiltersToQuery(jsonRequest, query);
			PreparedQuery preparedQuery = service().prepare(query);
			Iterator<Entity> it = preparedQuery.asIterator();
			while(it.hasNext()) {
				entity = it.next();
				JSON row = rows.addArrayElement();
				entityToJSON(entity, row);
			}
		}
	}
	
	public Entity executeListByKey(String key) {
		Entity entity = getByKey("key");
		return entity;
	}
	
	public List<Entity> executeListByQuery(WindQuery windQuery) {
		Query appEngineQuery = windQueryToAppEngineQuery(windQuery);
		PreparedQuery preparedQuery = service().prepare(appEngineQuery);
		List<Entity> entities = new ArrayList<Entity>();
		Iterator<Entity> it = preparedQuery.asIterator();
		while(it.hasNext()) {
			Entity entity = it.next();
			entities.add(entity);
		}
		return entities;
	}
	
	private Query windQueryToAppEngineQuery(WindQuery windQuery) {
		Query appEngineQuery = new Query(windQuery.getType().getCode());
		for(String sort : windQuery.sort()) {
			appEngineQuery.addSort(sort);
		}
		for(WindQueryFilter filter : windQuery.filters()) {
			appEngineQuery.addFilter(filter.getName(), getAppEngineOperatorFromString(filter.getOperator()), filter.getValue());
		}
		return appEngineQuery;
	}
	
	private Entity windEntityToAppEngineEntity(WindEntity windEntity) {
		Entity appEngineEntity = null;

		String type = windEntity.getType().getCode();

		String strKey = windEntity.getAttribute("key");
		if(strKey!=null) {
			appEngineEntity = getByKey(strKey);
		} else {
			appEngineEntity = new Entity(type);
		}
		
		for(String attributeName : windEntity.attributeNames()) {
			String attributeValue = windEntity.getAttribute(attributeName);
			if(attributeValue.length()<=500) {
				appEngineEntity.setProperty(attributeName, attributeValue);
			} else {
				Text text = new Text(attributeValue);
				appEngineEntity.setProperty(attributeName, text);
			}
		}
		
		return appEngineEntity;
	}
	
	private static void entityToJSON(Entity entity, JSON jsonEntity) {
		jsonEntity.addAttribute("type", getType(entity));
		jsonEntity.addAttribute("key", KeyFactory.keyToString(entity.getKey()));
		
		Map<String,Object> properties = entity.getProperties();
		Iterator<String> it = properties.keySet().iterator();
		while(it.hasNext()) {
			String name = it.next();
			Object value = properties.get(name);
			if(value instanceof String) {
				String str = (String)value;
				str = escapeJavaScript(str);
				jsonEntity.addAttribute(name, str);
			} else if (value instanceof Text) {
				Text text = (Text)value;
				String str = (String)text.getValue();
				str = escapeJavaScript(str);
				jsonEntity.addAttribute(name, str);
			}
		}
	}
	
	
	private static Entity getByKey(String strKey) {
		Entity entity = null;
		if(strKey!=null && strKey.length()>0) {
			Key key = KeyFactory.stringToKey(strKey);
			entity = getByKey(key);
		}
		return entity;
	}
	
	private static Entity getByKey(Key key) {
		Entity entity = null;
		try {
			entity = service().get(key);
		} catch (EntityNotFoundException ignore) {
		}
		return entity;
	}

	private static void addRequestSortsToQuery(JSON request, Query query) {
		String sort = request.getString("sort");
		if(sort!=null && sort.length()>0) {
			query.addSort(sort);
		}
	}

	private static void addRequestFiltersToQuery(JSON request, Query query) {
		JSON filters = request.get("filters");
		if(filters!=null) {
			for(int i=0; i<filters.getChildren().size(); i++) {
				JSON filter = filters.getChildren().get(i);
				String property = filter.getString("property");
				String operator = filter.getString("operator");
				String value = filter.getString("value");
				if(operator.equals("=") || operator.equals("==")) {
					query.addFilter(property, FilterOperator.EQUAL, value);
				} else if (operator.equals("<>") || operator.equals("!=")) {
					query.addFilter(property, FilterOperator.NOT_EQUAL, value);
				} else if (operator.equals("<")) {
					query.addFilter(property, FilterOperator.LESS_THAN, value);
				} else if (operator.equals("<=")) {
					query.addFilter(property, FilterOperator.LESS_THAN_OR_EQUAL, value);
				} else if (operator.equals(">")) {
					query.addFilter(property, FilterOperator.GREATER_THAN, value);
				} else if (operator.equals(">=")) {
					query.addFilter(property, FilterOperator.GREATER_THAN_OR_EQUAL, value);
				} 
			}
		}
	}
	
	private FilterOperator getAppEngineOperatorFromString(String windFilterOperator) {
		FilterOperator appEngineFilterOperator = null;
		if(windFilterOperator.equals("=") || windFilterOperator.equals("==")) {
			appEngineFilterOperator = FilterOperator.EQUAL;
		} else if (windFilterOperator.equals("<>") || windFilterOperator.equals("!=")) {
			appEngineFilterOperator = FilterOperator.NOT_EQUAL;
		} else if (windFilterOperator.equals("<")) {
			appEngineFilterOperator = FilterOperator.LESS_THAN;
		} else if (windFilterOperator.equals("<=")) {
			appEngineFilterOperator = FilterOperator.LESS_THAN_OR_EQUAL;
		} else if (windFilterOperator.equals(">")) {
			appEngineFilterOperator = FilterOperator.GREATER_THAN;
		} else if (windFilterOperator.equals(">=")) {
			appEngineFilterOperator = FilterOperator.GREATER_THAN_OR_EQUAL;
		} else {
			throw new RuntimeException("Invalid operator: " + windFilterOperator);
		}
		return appEngineFilterOperator;
	}
	
	private static String escapeJavaScript(String in) {
		String out = in;
		out = out.replace("\\", "&#92;");
		return out;
	}
	

	public static String getType(Entity entity) {
		String kind = entity.getKind();
		return kind;
	}
}
